背景

记录是我的原始需求之一。每隔一段时间,我必须记录一下自己的所思所感,所见所闻,否则就会感到空虚和不安。

以前也断断续续写过日记,但终归没形成习惯。究其原因,除了个人毅力,还有几点客观因素。

  1. 书写是缓慢且严肃的,想要记录的内容却不一定。
  2. 难以更改、查阅。
  3. 专业相关的内容很难用纸笔记录。
  4. 我的字不是很好看。

但除了纸笔,我当时也没得选。后来上大学有了属于自己的电脑,好算用上了先进的记录工具。

我记得当时用过一段时间的OneNote。OneNote内容非常灵活,但排版耗时>内容耗时也着实给我上了一课,以至于后来初次接触Markdown(的设计理念)之后就再也没回去过。期间也尝试过语雀等在线工具,但因为种种原因没有继续使用,最终选择了Obsidian。

Obsidian非常好地满足了我的一部分记录需求,它很适合记录中长长度的内容。但当我只想吐个槽、发个牢骚的时候,它就不是那么合用了。这类需求,人们通常会选择跟朋友聊,或者发微博、推特。但一来有些想法只想记录,不想交流,二来微博推特这种垃圾场我本就避之不及,更不用说使用了。

前段时间工作压力非常大,以至于我不得不在下班时间忙里偷闲,研究些自己感兴趣的东西回复下心情。于是这个问题就被提上了日程。其实我也不是第一次搜索它相关的资料,心里大概清楚,如果是我自己实现的话,大概就是一个极简版的Headless CMS + iOS前端App。问题在于,如果放到两年前,我可能还挺有这么搞的兴致,但现在真的太忙了。我不能白天上班,晚上加班,回家还写这种东西。原型实现起来肯定不麻烦,但想要合用,不知道要打磨多久。

又google了一段时间,发现有个概念很合我意:去中心化交流平台。它的代表作Mastodon我居然听说过,也好奇过,但从来没有去查过具体是什么东西。“交流平台”是指这种工具就类似于Self-host的博客或者微博,而“去中心化”就比较有意思了,是指每个人都可以自己启动自己的Mastodon实例,该实例里所有用户数据全部存储在本地,但这些实例之间又可以通过ActivityPub协议交流,使得一个实例里的用户可以跟远在天边的另一个实例中的用户互动,例如@,对其微博点赞,或进行转发。

(甚至连去中心化的问题也得到了完美继承:如果你的推文被其他实例的用户转载,你可能再也无法彻底删除这个推文,因为即便本地删除了,别的实例还是有的。)

这真是个绝妙的主意!隐私、安全、有限却又可以不受限的交流,我要的就是这个。至于群体属性,少邀请或者不邀请不就可以了嘛。

说干就干。Mastodon对我来说太重了,经过一番比较,我选择了Misskey。除了轻量级、功能齐全之外,它还很二次元,吉祥物是个纸片人(啊?),整体风格也非常符合我的审美。

到这里背景就交代完了,下面整理心情,回归主体,记录下部署过程中的一些坑。

部署

正如这段时间接触过的大部分开源软件一样,Misskey没有稳定、官方的docker镜像。官方帮助文档提供了docker build + docker compose的部署方式,但有亿点点部分需要修改。

首先说明,Misskey基于node.js开发,而我对node.js一窍不通,所以有些问题的处理方式可能比较奇怪,概念理解也可能并不准确,但最终是可以部署成功的。

docker build这边主要有两个问题,一个是apt源,一个是npm/pnpm/corepack源。

apt源的问题比较好理解,在Dockerfile里加一下即可:

RUN echo "deb http://mirrors.tencentyun.com/debian bullseye main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-updates main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian-security bullseye-security main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-backports main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-proposed-updates main contrib non-free" > /etc/apt/sources.list

npm/pnpm/corepack源就相对麻烦一点。简单来说,Misskey总共构建了三四个镜像,这些镜像的FROM都是有npm的,但构建过程却不是直接使用npm,而是使用corepack调用的pnpm。

npm和pnpm都是前端的构建工具,类似于Gradle。而corepack是构建工具的一层封装,比如在指定了构建工具使用pnpm之后,corepack build就会调用pnpm build,这样进行构建工具的切换也很方便。

这几个镜像都是指定了使用pnpm的,实际构建使用corepack。但corepack下载pnpm是从官方网站下的,速度非常慢,且因为在docker build过程中,不是非常好搭梯子,所以我采用了下面的策略:

  1. 既然这几个docker build的FROM都自带npm,所以先给npm换源。
  2. 因为最终是使用corepack调用pnpm,所以可以替换成使用npm安装pnpm,再给pnpm换源,最后将corepack命令直接替换为pnpm命令。

这样就跳过了corepack这个不好伺候的大爷,具体配置如下:

# corepack enable替换成:
RUN npm config set registry https://registry.npmmirror.com
RUN npm install -g pnpm
RUN pnpm config set registry https://registry.npmmirror.com

# corepack install替换成:
RUN pnpm install

修改后的完整Dockerfile如下(2024.7.0版本):

# syntax = docker/dockerfile:1.4

ARG NODE_VERSION=20.16.0-bullseye

# build assets & compile TypeScript

FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder

RUN npm config set registry https://registry.npmmirror.com
RUN npm install -g pnpm
RUN pnpm config set registry https://registry.npmmirror.com

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
        --mount=type=cache,target=/var/lib/apt,sharing=locked \
        rm -f /etc/apt/apt.conf.d/docker-clean \
        ; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
        && apt-get update \
        && apt-get install -yqq --no-install-recommends \
        build-essential

WORKDIR /misskey

COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"]
COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
COPY --link ["packages/sw/package.json", "./packages/sw/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]

ARG NODE_ENV=production

RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
        pnpm i --frozen-lockfile --aggregate-output

COPY --link . ./

RUN git submodule update --init
RUN pnpm build

# build native dependencies for target platform

FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder

RUN echo "deb http://mirrors.tencentyun.com/debian bullseye main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-updates main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian-security bullseye-security main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-backports main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-proposed-updates main contrib non-free" > /etc/apt/sources.list

RUN apt-get update \
        && apt-get install -yqq --no-install-recommends \
        build-essential

RUN npm config set registry https://registry.npmmirror.com
RUN npm install -g pnpm
RUN pnpm config set registry https://registry.npmmirror.com

WORKDIR /misskey

COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"]
COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]

ARG NODE_ENV=production

RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
        pnpm i --frozen-lockfile --aggregate-output

FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner

ARG UID="991"
ARG GID="991"

RUN npm config set registry https://registry.npmmirror.com
RUN npm install -g pnpm
RUN pnpm config set registry https://registry.npmmirror.com

RUN echo "deb http://mirrors.tencentyun.com/debian bullseye main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-updates main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian-security bullseye-security main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-backports main contrib non-free" > /etc/apt/sources.list \
        echo "deb http://mirrors.tencentyun.com/debian bullseye-proposed-updates main contrib non-free" > /etc/apt/sources.list

RUN apt-get update \
        && apt-get install -y --no-install-recommends \
        ffmpeg tini curl libjemalloc-dev libjemalloc2 \
        && ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
        && groupadd -g "${GID}" misskey \
        && useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \
        && find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
        && find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists

USER misskey
WORKDIR /misskey

# add package.json to add pnpm
COPY --chown=misskey:misskey ./package.json ./package.json

COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules
COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/built ./packages/misskey-js/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./

ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so
ENV NODE_ENV=production
HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/misskey/healthcheck.sh"]
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["pnpm", "run", "migrateandstart"]

除此之外,因为云服务器上clone Misskey git repo比较慢,所以是本地clone好再传上去的。clone和checkout的时候记得要加--rescurse-submodules(Misskey有个fluent-emoji子模块)。

构建完成后,按照官方文档正常docker compose up -d即可。这里还需要额外执行下chown -hR 991.991 ./files,否则files目录挂载给容器会有权限问题,详见 https://github.com/misskey-dev/misskey/issues/9564

最后是配置代理,别忘记在每一层代理都配置好websocket:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

部署成功(忽略测试用的实例名称、登录页背景图,以及我不知道怎么关闭的联邦展示):

2024-08-02 Misskey-20240802.png

主界面:

2024-08-02 Misskey-20240802-1.png

更新

见:https://misskey-hub.net/en/docs/for-admin/install/guides/docker/#updating-misskey

如果需要挂梯子,见:https://secure.shadowsocks.au/knowledgebase/160/Linux命令行使用教程.html

结语

不知为什么,我对Misskey最发自内心的评价居然是“可爱”?

抛开UI不谈,根本上应该是因为它满足了我的记录需求吧,可能记录欲本身就是一种变相的交流欲,只不过从希望跟别人交流变成了希望跟自己交流。

噢对了,我准备给我的网站做一个首页,放上我的博客和Misskey链接,并用于以后扩充。等这段时间忙过去就准备动手!